﻿using System.Collections.Generic;
using SlimDX;
using SlimDX.Direct3D10;

namespace SGX
{
    internal enum TechniqueType
    {
        Default,
        Textured,
        TexturedWithSpecular,
        NormalTextured,
        NormalTexturedWithSpecular
    }

    class MeshNode : SceneNode
    {
        SlimDX.Direct3D10.Mesh m_mesh;
        Material[] m_materialList;

        EffectTechnique m_Technique;
        TechniqueType m_TechniqueType;
        EffectState m_EffectState;
        int m_inputLayoutSize;
        InputLayout m_Inputlayout;

        public MeshNode(string name, InputElement[] elements, string positionElement, int vertexCount, int faceCount, MeshFlags flags)
            : base(name)
        {
            m_mesh = new Mesh(GlobalResourceCache.Device, elements, positionElement, vertexCount, faceCount, flags);
            m_materialList = null;
            m_inputLayoutSize = elements.Length;
            m_Inputlayout = new InputLayout(GlobalResourceCache.Device, m_EffectState.Effect.GetTechniqueByName("PNTT_withSpecularMap").GetPassByIndex(0).Description.Signature, elements);

            m_EffectState = GetorCreateEffectState();
            m_EffectState.LoadEffect("shaders\\shaderStatic.hlsl", "shaders\\sharedPool.hlsl");
            m_EffectState.AddShaderVariable(ShaderVariableType.Matrix, "gWorld");
            m_EffectState.AddShaderVariable(ShaderVariableType.Resource, "gDiffuseMap");
            m_EffectState.AddShaderVariable(ShaderVariableType.Resource, "gNormalMap");
            m_EffectState.AddShaderVariable(ShaderVariableType.Resource, "gSpecularMap");
            m_EffectState.AddShaderVariable(ShaderVariableType.Vector, "matDiffuse");
            m_EffectState.AddShaderVariable(ShaderVariableType.Vector, "matSpecular");

            m_Technique = m_EffectState.Effect.GetTechniqueByName("PN");
            m_TechniqueType = TechniqueType.Default;
            
        }

        public Result SetMeshAttributes(MeshAttributeRange[] attributes)
        {
            Result res = m_mesh.SetAttributeTable(attributes);
            if (res.IsFailure)
                return res;
            return ResultCode.Success;
        }

        public void SetMaterials(Material[] materials)
        {
            m_materialList = materials;
            if (m_inputLayoutSize == 3)
            {
                if (materials[0].diffuseRV == null)
                    m_TechniqueType = TechniqueType.Default;
                else if (materials[0].specularRV == null)
                    m_TechniqueType = TechniqueType.Textured;
                else
                    m_TechniqueType = TechniqueType.TexturedWithSpecular;
            }
            else if (m_inputLayoutSize == 4)
            {
                if (materials[0].normalRV == null)
                {
                    if (materials[0].diffuseRV == null)
                        m_TechniqueType = TechniqueType.Default;
                    else if (materials[0].specularRV == null)
                        m_TechniqueType = TechniqueType.Textured;
                    else
                        m_TechniqueType = TechniqueType.TexturedWithSpecular;
                }
                else
                {
                    if (materials[0].diffuseRV == null)
                        m_TechniqueType = TechniqueType.Default;
                    else if (materials[0].specularRV == null)
                        m_TechniqueType = TechniqueType.NormalTextured;
                    else
                        m_TechniqueType = TechniqueType.NormalTexturedWithSpecular;
                }
            }
        }

        public Mesh Mesh
        {
            get { return m_mesh; }
        }

        public Material[] Materials
        {
            get { return m_materialList; }
        }

        public override void Dispose()
        {
            if(!m_mesh.Disposed)
                m_mesh.Dispose();
            base.Dispose();
        }

        public override void PreRender(Scene scene)
        {
            GlobalResourceCache.Device.InputAssembler.SetInputLayout(m_Inputlayout);
            m_EffectState.SetMatrix("gWorld", scene.MatrixStack.Top);
        }

        public override void Render(Scene scene)
        {
            base.Render(scene);
            switch (m_TechniqueType)
            {
                case TechniqueType.Default:
                    m_EffectState.Set("matDiffuse", new Vector4(0.5f, 0.5f, 0.5f, 1.0f));
                    m_EffectState.Set("matSpecular", new Vector4(1.0f, 1.0f, 1.0f, 2.0f));
                    break;
                case TechniqueType.Textured:
                    m_EffectState.SetResource("gDiffuseMap", m_materialList[0].diffuseRV);
                    m_EffectState.Set("matSpecular", new Vector4(1.0f, 1.0f, 1.0f, 2.0f));
                    break;
                case TechniqueType.TexturedWithSpecular:
                    m_EffectState.SetResource("gDiffuseMap", m_materialList[0].diffuseRV);
                    m_EffectState.SetResource("gSpecularMap", m_materialList[0].specularRV);
                    break;
                case TechniqueType.NormalTextured:
                    m_EffectState.SetResource("gDiffuseMap", m_materialList[0].diffuseRV);
                    m_EffectState.Set("matSpecular", new Vector4(1.0f, 1.0f, 1.0f, 2.0f));
                    m_EffectState.SetResource("gNormalMap", m_materialList[0].normalRV);
                    break;
                case TechniqueType.NormalTexturedWithSpecular:
                    m_EffectState.SetResource("gDiffuseMap", m_materialList[0].diffuseRV);
                    m_EffectState.SetResource("gSpecularMap", m_materialList[0].specularRV);
                    m_EffectState.SetResource("gNormalMap", m_materialList[0].normalRV);
                    break;
            }

            EffectPass pass = m_Technique.GetPassByIndex(0);
            pass.Apply();
            m_mesh.DrawSubset(0);
        }

        public override void PostRender(Scene scene)
        {
            base.PostRender(scene);
            //TODO
        }
    }

    public struct Material
    {
        string Name { get; set; }

        string diffuseTexturePath;
        string NormalTexturePath;
        string SpecularTexturePath;

        /*
        public Vector4 diffuse;
        public Vector4 ambient;
        public Vector4 specular;
        public float power;
        */

        public ShaderResourceView diffuseRV;
        public ShaderResourceView normalRV;
        public ShaderResourceView specularRV;

        public void LoadTextures()
        {
            diffuseRV = GlobalResourceCache.CreateTextureFromFile(diffuseTexturePath, "");

        }
    }
}
